import hmac
import json
import logging
from datetime import datetime
from django.conf import settings
from django.http import QueryDict
from common.user import db as admin_user
from common.merchant import merchant_db
from common.merchant import role_db
from common.merchant import handler as merchant_handler
from common.permission import db as perm_db
from common.permission.models import RequestMethod
from common.utils import JsonResponse
from common.const import ResponseStatus
from common.utils.maestro_exceptions import MaestroException

_LOGGER = logging.getLogger(__name__)

sign_pairs = {
    's2s': 'b5384a02ab10ba532b9e611fcf15c08e83b39c1c',
    'loki': 'b5384a02ab10ba532b9e611fcf15c08e83b39c1c',
    'quanmdl': '10504a02ab10ba532b9e611fcf15c08e83b39c1c',
    'friday': 'b5384a02ab10ba532b9e611fcf15c08e83b39c1c',
    'agent': 'b5384a02ab10ba532b9e611fcf15c08e83b39c1c',
    'qk': '56984a02ab10b1234b9e611fcf15c08e83b39c1c',
    'nasa': '6d781728f194e68949da7d613c823776',
    'op_dark3': '6d781728f1977931949da7d613c823776',
    'op_dark4': '77511728f1977931949da7d613c823776',
    'bot': '53055d6a763d544a14954f39baf8a9ad',
    'mf_pay': '245d648e9ea8cc006ac828335a7af8d9',
}


def check_perm(url_array, request_method, allocated_permission):
    allocated_permission_list = allocated_permission.split('|')
    length = len(url_array)
    while length >= 1:
        url = '/'.join(url_array[0:length])
        if not url.endswith('/'):
            url += '/'
        permission_inst = perm_db.get_perm({"url": url, "request_method": request_method})
        if permission_inst:
            if permission_inst.allowed_page_id == '0':
                return True
            allow_page_id_list = permission_inst.allowed_page_id.split('|')
            for page_id in allocated_permission_list:
                if page_id != '' and page_id in allow_page_id_list:
                    return True
        length -= 1
    return False


class BaseMiddleware(object):
    @staticmethod
    def process_exception(request, exception):
        if isinstance(exception, MaestroException):
            response = dict(
                status=exception.error_code.code,
                msg=exception.detail_message,
                timestamp=datetime.now(),
            )
        else:
            response = dict(
                status=-1,
                msg='内部错误，请联系管理员',
                timestamp=datetime.now(),
            )
        _LOGGER.exception('catch error {} in {}, uid:{}'.format(exception.__class__.__name__, request.path,
                                                                request.user_id if hasattr(request,
                                                                                           'user_id') else 'null'))
        return JsonResponse(response, status=500)


class UserMiddleware(BaseMiddleware):
    """ maestro admin api permission & merchant verification"""

    @staticmethod
    def check_user_api_validation(method, path, allocated_permission):
        url_array = path.split('/')
        if method == 'GET':
            request_method = RequestMethod.GET.value
        else:
            request_method = RequestMethod.OTHERS.value
        if not check_perm(url_array, request_method, allocated_permission):
            return False
        return True

    @staticmethod
    def get_user_merchant_role(merchant_id, user_role):
        user_role_list = [int(role_id) for role_id in user_role.split(',')[1:-1]]
        role = role_db.get_role({'id__in': user_role_list, 'merchant_id': merchant_id})
        return role

    def process_request(self, req):
        if req.method == 'GET':
            req.DATA = QueryDict(req.META['QUERY_STRING'])
        else:
            data = json.loads(req.body.decode())
            req.DATA = QueryDict('', mutable=True)
            req.DATA.update(data)
        user_id, token = req.META.get('HTTP_X_AUTH_USER'), req.META.get('HTTP_X_AUTH_TOKEN')
        perm_response = JsonResponse(dict(status=ResponseStatus.FAIL.value, msg=str('权限不足')), status=500)
        if user_id and token:
            req.user_id = req.user = None
            info = admin_user.get_user_token(user_id, token)
            if info and info.deleted == 0:
                if datetime.timestamp(datetime.now()) - datetime.timestamp(
                        info.updated_at) > settings.TOKEN_VOIDED_TIME:
                    admin_user.logout_user(user_id)
                    return JsonResponse(dict(status=ResponseStatus.TOKEN_VOIDED.value, msg=str('登录已过期')), status=500)
                user = admin_user.get_user(user_id)
                user_role_level_dct = merchant_handler.get_user_roles_information(user_id)
                req.user_id, req.user = user_id, user
                req.is_root = user_role_level_dct['is_root']
                req.merchant_id, req.merchant_name, req.es_index = None, None, None
                if req.path.startswith('/admin/'):
                    if not user:
                        return perm_response
                    validation = self.check_user_api_validation(req.method, req.path, '') if not req.is_root else True
                    if not validation:
                        return perm_response
                else:
                    req.merchant_name = req.path.split('/')[1]
                    req.es_index = req.merchant_name
                    merchant = merchant_db.get_merchant(dict(merchant_name=req.merchant_name))
                    if not merchant:
                        return perm_response
                    req.merchant_id = merchant.id
                    if not req.is_root:
                        role = self.get_user_merchant_role(req.merchant_id, user.role)
                        if not role:
                            return perm_response
                        req.merchant_role = role.__dict__
                        validation = self.check_user_api_validation(req.method, req.path, role.perm)
                        if not validation:
                            return perm_response
                    else:
                        req.merchant_role = user_role_level_dct['root']
            admin_user.token_time_recode(user_id)
                

class ApiSignMiddleware(BaseMiddleware):
    """ Verify api parameter sign if not valid,  deny it """

    def process_request(self, req):
        if not req.path.startswith('/api/'):
            return
        perm_response = JsonResponse(dict(status=ResponseStatus.FAIL.value, msg=str('权限不足')), status=500)
        if req.method == 'GET':
            query_dict = req.GET.dict()
        else:
            query_dict = json.loads(req.body)
        if 'key' not in query_dict or 'sign' not in query_dict:
            return perm_response
        key = query_dict['key']
        origin_sign = query_dict.pop('sign')
        secret = self.get_secret_by_key(key)
        if secret is None:
            return perm_response
        sign = self.generate_sign(query_dict, secret)
        if origin_sign != sign and origin_sign != 'maestro_secret':
            return perm_response
        req.platform_id = key

    @staticmethod
    def get_secret_by_key(key):
        if key not in sign_pairs:
            return None
        return sign_pairs.get(key)

    @staticmethod
    def generate_sign(params, secret):
        s = ''
        for k in sorted(params.keys()):
            s += '%s=%s&' % (k, params[k])
        s = s[:-1]
        m = hmac.new(secret.encode('utf8'), s.encode('utf8'))
        sign = m.hexdigest().upper()
        return sign
